const PENDING = "pending";
const REJECTED = "reject";
const FULLFILLED = "fullfilled";

class MyPromise {
  constructor(excute) {
    this.state = PENDING;
    this.value = "";
    this.reason = "";
    this.onFulfilledFns = [];
    this.onRejectedFns = [];

    function resolve(value) {
      if (this.state === PENDING) {
        queueMicrotask(() => {
          this.state = FULLFILLED;
          this.value = value;
          this.onFulfilledFns.forEach((f) => {
            f(value);
          });
        });
      }
    }

    function reject(reason) {
      if (this.state === PENDING) {
        queueMicrotask(() => {
          this.state = REJECTED;
          this.reason = reason;
          this.onRejectedFns.forEach((f) => {
            f(reason);
          });
        });
      }
    }

    try {
      excute(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  /**
   * then返回一个promoise 支持链式调用
   * @param {*} onResolve
   * @param {*} onReject
   */
  then(onResolve, onReject) {
    let promsie;
    let x;
    // 已经不是pending状态，直接resolve或reject
    if (this.state === FULLFILLED) {
      promise = new MyPromise((resolve, reject) => {
        try {
          setTimeout(() => {
            x = onResolve(this.value);
            resolvePromise(promsie, x, resolve, reject);
          }, 0);
        } catch (reason) {
          reject(reason);
        }
      });
    } else if (this.state === REJECTED) {
      promise = new MyPromise((resolve, reject) => {
        try {
          setTimeout(() => {
            x = onReject(this.reason);
            resolvePromise(promsie, x, resolve, reject);
          }, 0);
        } catch (reason) {
          reject(reason);
        }
      });
    } else if (this.state === PENDING) {
      promsie = new MyPromise((resolve, reject) => {
        this.onFulfilledFns.push(() => {
          try {
            setTimeout(() => {
              x = onResolve(this.value);
              resolvePromise(promsie, x, resolve, reject);
            }, 0);
          } catch (reason) {
            reject(reason);
          }
        });

        this.onRejectedFns.push(() => {
          try {
            setTimeout(() => {
              x = onReject(this.reason);
              resolvePromise(promsie, x, resolve, reject);
            }, 0);
          } catch (reason) {
            reject(reason);
          }
        });
      });
    }
    return promise;
  }
}

function resolvePromise(promise, x, resolve, reject) {
  if (promise === x) {
    throw new Error("then返回的promise不能是自己，要卡死");
  }

  if (x instanceof MyPromise) {
    if (x.state === FULLFILLED) {
      resolve(x.value);
    } else if (x.state === REJECTED) {
      reject(x.reason);
    } else if (x.state === PENDING) {
      x.then((y) => {
        resolvePromise(promise, y, resolve, reject); // 就是背下来
      });
    }
  } else if (x !== null && (typeof x === "object" || typeof x === "function")) {
    let excuted;
    try {
      const then = x.then;
      // thenable处理
      if (typeof then === "function") {
        then.call(
          x,
          (y) => {
            if (excuted) return;
            excuted = true;
            resolvePromise(promise, y, resolve, reject);
          },
          (e) => {
            if (excuted) return;
            excuted = true;
            reject(e);
          }
        );
      }
    } catch (reason) {
      if (excuted) return;
      excuted = true;
      reject(reason);
    }
  } else {
    resolve(x);
  }
}

function* gg() {
  yield 1;
  const p1 = yield 2;
  console.log(p1);
  const p2 = yield 3;
  console.log(p2);
  return 4;
}

function gen2Async(genFn) {
  return function () {
    const gen = genFn.apply(this, [...arguments]);
    return new Promise(resolve, (reject) => {
      function go(method, val) {
        let res;
        try {
          res = gen[method](val);
        } catch (err) {
          reject(err);
        }

        const { done, value } = res;
        if (!done) {
          return Promise.resolve(value).then(
            (data) => {
              go["next"](data);
            },
            (err) => {
              go["throw"](err);
            }
          );
        } else {
          resolve(value);
        }
      }

      go("next");
    });
  };
}
